#include "cupe.h"

UART_HandleTypeDef huart1;

#if DEBUG_ON == 1

// 重定向printf start ----------------------------------------------------------
// _write函数在syscalls.c中，使用__weak定义， 所以可以直接在其他文件中定义_write函數
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
  return (HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xffff) != HAL_OK) ? 0 : len;
}
// 重定向printf end -------------------------------------------------------------

#endif

// USART1初始化函数
HAL_StatusTypeDef MX_USART1_UART_Init(int baudrate)
{
  // 使能串口时钟
  __HAL_RCC_USART1_CLK_ENABLE();

  // 配置PA9为USART1_TX
  GPIO_InitTypeDef GPIO_InitStruct; // = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  huart1.Instance = USART1;                    // 指定USART1实例
  huart1.Init.BaudRate = baudrate;             // 设置波特率（根据需要调整）
  huart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位为8位
  huart1.Init.StopBits = UART_STOPBITS_1;      // 设置停止位为1位
  huart1.Init.Parity = UART_PARITY_NONE;       // 设置无奇偶校验
  huart1.Init.Mode = UART_MODE_TX_RX;          // 设置模式为发送和接收
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置无硬件流控制

  return HAL_UART_Init(&huart1);
}

// 系统时钟配置
HAL_StatusTypeDef SystemClock_Config(void)
{
  // 初始化RCC振荡器，配置PLL等（具体参数根据你的硬件和需求来设置）
  RCC_OscInitTypeDef RCC_OscInitStruct; // = {0};
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  HAL_StatusTypeDef ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
  if (ret != HAL_OK)
  {
    return ret;
  }

  // 初始化CPU、AHB和APB总线时钟
  RCC_ClkInitTypeDef RCC_ClkInitStruct; // = {0};
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  return HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

void cupe_init(void)
{
  HAL_Init();

  SystemClock_Config();

  // 使能GPIOA、GPIOC、GPIOE时钟
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStruct; // = {0};

  // PE0 VCC引脚设置为推挽输出模式、不上拉、最大速度
  GPIO_InitStruct.Pin = VCC_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(VCC_GPIO_PORT, &GPIO_InitStruct);

  // PE1 开关引脚设置为输入模式、上拉
  GPIO_InitStruct.Pin = PWR_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(PWR_GPIO_PORT, &GPIO_InitStruct);

  // PA5 LED引脚设置为推挽输出模式、上拉、最大速度
  GPIO_InitStruct.Pin = LED_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);

  // 等待1us
  HAL_Delay(72);

  // PE0 供电引脚电平拉高，使能外部供电
  HAL_GPIO_WritePin(VCC_GPIO_PORT, VCC_PIN, GPIO_PIN_SET);

  // 等待直到开关按钮释放(意味着释放电源按钮后系统才继续启动)
  while (HAL_GPIO_ReadPin(PWR_GPIO_PORT, PWR_PIN) == GPIO_PIN_SET)
    ;

#if DEBUG_ON == 1
  // 初始化USART1
  MX_USART1_UART_Init(115200);
#endif
}

// 电源开关按键检测函数
int cupe_power_key(void)
{
  // 读取PWR_PIN的状态以确认按钮是否被按下（低电平）
  if (HAL_GPIO_ReadPin(PWR_GPIO_PORT, PWR_PIN) == GPIO_PIN_SET)
  {
    // 延时以消除抖动
    HAL_Delay(10);

    // 再次读取PWR_PIN的状态以确认按钮仍然被按下
    if (HAL_GPIO_ReadPin(PWR_GPIO_PORT, PWR_PIN) == GPIO_PIN_SET)
    {

      // 如果按钮仍然被按下（低电平），则等待释放（上升沿）
      while (HAL_GPIO_ReadPin(PWR_GPIO_PORT, PWR_PIN) == GPIO_PIN_SET)
        ;
      return 1;
    }
  }
  return 0;
}

// PE0 供电引脚电平拉低，禁止外部供电(关机)
void cupe_shutdown()
{
  HAL_GPIO_WritePin(VCC_GPIO_PORT, VCC_PIN, GPIO_PIN_RESET);
}